import os
import sys
import time
import numpy as np
import functools
import pydicom
import cv2
from pydicom.tag import Tag

__all__ = ['read_dcm_by_pydicom',
           'read_dcm_info',
           'read_dcm_to_norm',
           'read_dcm_to_8bit',
           'convert_dcm_grayscale8',
           'dicom_convert_jpg']

PHOTOMETRIC_INTERPRETATION_TAG = (0x0028,0x0004)
BITS_STORED_TAG = (0x0028, 0x0101)
RESCALE_SLOPE_TAG = (0x0028,0x1053)
RESCALE_INTERCEPT_TAG = (0x0028,0x1052)
BODY_PART_TAG = (0x0018, 0x0015)
WINDOW_CENTER = (0x0028, 0x1050)
WINDOW_WIDTH = (0x0028, 0x1051)
PIXEL_SPACING_TAG = (0x0028,0x0030)


BODY_PART_NAME = ('BREAST', 'LSPINE', 'LSPINE', 'SKULL', 'ANKLE', 'HAND', 'KNEE',
                  'ABDOMEN', 'CSPINE', 'C-SPINE','SHOULDER', 'FOOT', 'CHEST',
                  'HIP', 'THORAX', 'COCCYX', 'ELBOW', 'LEG')

CHEST_TAG_NAME = ('胸', 'CHEST', 'THORAX')

sys_is_little_endian = (sys.byteorder == 'little')


def dicom_convert_jpg(dicom_path, jpg_path):
    if not os.path.exists(dicom_path):
        return -1
    image = read_dcm_to_8bit(dicom_path)
    # Convert to uint
    image = np.uint8(image)
    cv2.imwrite(jpg_path, image)


def create_hu_lookup(bits):
    lookup = [i for i in range(1<<bits)]
    return lookup


def read_dcm_by_pydicom(filename):
    dcm = pydicom.dcmread(filename)
    # try:
    #     dcm = pydicom.dcmread(fileanme)
    # except Exception as e:
    #     print('Error:', e, fileanme)
    #     return dcm_info

    try:
        dcm.decompress()
    except Exception as e:
        print('Error:', e, filename)
        return None
    return dcm

def read_dcm_info(filename):
    """
    read dicom file
    :param fileanme:dicom file name
    :param dcm_info:dict
    :return:dict
    """
    dcm_info = {}
    dcm_info['image'] = None

    if not os.path.exists(filename):
        return dcm_info

    dcm = read_dcm_by_pydicom(filename)
    if dcm is None:
        return dcm_info

    dcm_info['Modality'] = dcm.Modality
    dcm_info['PatientSex'] = dcm.PatientSex

    if Tag(BODY_PART_TAG) in dcm:
        body_name = dcm.BodyPartExamined
        if body_name not in BODY_PART_NAME:
            encode_name = body_name.encode(encoding="iso8859")
            body_name = encode_name.decode('gbk')
        dcm_info['body_part'] = body_name # str(dcm[BODY_PART_TAG[0], BODY_PART_TAG[1]]._value).upper()
    else:
        dcm_info['body_part'] = ''

    # if Tag(PIXEL_SPACING_TAG) in dcm:
    #     dcm_info['spacing'] = list(map(float, dcm[PIXEL_SPACING_TAG[0], PIXEL_SPACING_TAG[1]]._value))
    # else:
    #     dcm_info['spacing'] = None

    if Tag(PHOTOMETRIC_INTERPRETATION_TAG) in dcm:
        dcm_info['photo_metric_interpretation'] = dcm[PHOTOMETRIC_INTERPRETATION_TAG[0], PHOTOMETRIC_INTERPRETATION_TAG[1]]._value
    else:
        dcm_info['photo_metric_interpretation'] = ''

    if Tag(BITS_STORED_TAG) in dcm:
        dcm_info['bits'] = int(dcm[BITS_STORED_TAG[0], BITS_STORED_TAG[1]]._value)
    else:
        dcm_info['bits'] = 8

    if Tag(RESCALE_SLOPE_TAG) in dcm:
        dcm_info['rescaleslope'] = int(dcm[RESCALE_SLOPE_TAG[0], RESCALE_SLOPE_TAG[1]]._value)
    else:
        dcm_info['rescaleslope'] = None

    if Tag(RESCALE_INTERCEPT_TAG) in dcm:
        dcm_info['rescaleintercept'] = int(dcm[RESCALE_INTERCEPT_TAG[0], RESCALE_INTERCEPT_TAG[1]]._value)
    else:
        dcm_info['rescaleintercept'] = None

    if hasattr(dcm, 'Rows'):
        dcm_info['rows'] = int(dcm.Rows)
    else:
        dcm_info['rows'] = 0
    if hasattr(dcm, 'Columns'):
        dcm_info['cols'] = int(dcm.Columns)
    else:
        dcm_info['cols'] = 0

    if hasattr(dcm, 'WindowCenter'):
        dcm_info['wc'] = int(dcm.WindowCenter)
    else:
        dcm_info['wc'] = 0
    if hasattr(dcm, 'WindowWidth'):
        dcm_info['ww'] = int(dcm.WindowWidth)
    else:
        dcm_info['ww'] = 0

    if hasattr(dcm, 'pixel_array'):
        dcm_info['image'] = dcm.pixel_array
    else:
        dcm_info['image'] = None
    return dcm_info


def convert_dcm_grayscale8(dcm_info, norm=False):
    """
    convert ct value to gray 8bit image
    :param dcm_info:
    :return:
    """
    img_data = None
    if dcm_info['image'] is not None:
        img_data = dcm_info['image'].astype(float)
        if dcm_info['wc'] != 0 and dcm_info['ww'] != 0:
            img_data = convert_fixed_ww_wc(img_data, dcm_info['ww'], dcm_info['wc'])
        else:
            img_data = convert_defalut_ww_wc(img_data)

        if not norm:
            img_data = np.uint8(img_data*255)
        if 'photo_metric_interpretation' in dcm_info and dcm_info['photo_metric_interpretation'].upper().find('MONOCHROME1') >= 0:
            if not norm:
                img_data = (255 - img_data)
            else:
                img_data = 1.0 - img_data
    return img_data


def ajuste_dcm_grayscale8(dcm_info, ww, wc):
    img_data = dcm_info['image'].astype(float)
    img_type = dcm_info['Modality'].upper()

    if ww != 0 and wc != 0:
        img_data = convert_fixed_ww_wc(img_data, ww, wc)
    else:
        img_data = convert_defalut_ww_wc(img_data)
    img_data = np.uint8(img_data * 255)
    if dcm_info['photo_metric_interpretation'].upper().find('MONOCHROME1') >= 0:
        img_data = (255 - img_data)

    return img_data


def convert_defalut_ww_wc(img_data):
    img_temp = img_data
    img_temp = (np.maximum(img_temp, 0) / img_temp.max()) #* 255.0
    return img_temp#np.uint8(img_temp)


def convert_fixed_ww_wc(img_data, ww, wc):
    img_temp = img_data
    img_temp.flags.writeable = True
    min_v = (2 * wc - ww) / 2.0 + 0.5
    max_v = (2 * wc + ww) / 2.0 + 0.5
    factor = 1.0 / (max_v - min_v) # 255.0 / (max_v - min_v)

    img_temp = (img_temp - min_v) * factor

    min_index = img_temp < 0
    img_temp[min_index] = 0
    max_index = img_temp > 1#255
    img_temp[max_index] = 1#255
    return img_temp


def getSlicesPixelHu(slices):
    """
    convert 2d dicom slices into CT value(int16)
    :param slices:2d dicom slices
    :return:3D CT image(n*w*h), spacing
    """
    image = np.stack([s.pixel_array for s in slices])

    # Convert to int16 (from sometimes int16),
    # should be possible as values should always be low enough (<32k)
    image = image.astype(np.int16)

    # Convert to Hounsfield units (HU)
    for slice_number in range(len(slices)):
        intercept = slices[slice_number].RescaleIntercept
        slope = slices[slice_number].RescaleSlope

        if slope != 1:
            image[slice_number] = slope * image[slice_number].astype(np.float64)
            image[slice_number] = image[slice_number].astype(np.int16)

        image[slice_number] += np.int16(intercept)

    return np.array(image, dtype=np.int16), np.array([slices[0].SliceThickness] + slices[0].PixelSpacing._list,
                                                     dtype=np.float32)


def convert_pixel_to_ct(dcm_info):
    if dcm_info['rescaleslope'] and dcm_info['rescaleintercept'] and dcm_info['image']:
        dcm_info['image'] = dcm_info['image'] * np.int16(dcm_info['rescaleslope']) + np.int16(dcm_info['rescaleintercept'])


def read_dcm_to_8bit(file):
    """
    Read dicom ct value convert grayscale 8bit image.
    :param file:
    :return:
    """
    dcm_info = read_dcm_info(file)
    convert_pixel_to_ct(dcm_info)
    return convert_dcm_grayscale8(dcm_info)


def read_dcm_to_norm(file):
    """
    Read dicom ct value convert grayscale normalized image.
    :param file:
    :return:
    """
    dcm_info = read_dcm_info(file)
    convert_pixel_to_ct(dcm_info)
    return convert_dcm_grayscale8(dcm_info, True)

def lumtrans(img):
    lungwin = np.array([-1200.,600.])
    newimg = (img-lungwin[0])/(lungwin[1]-lungwin[0])
    newimg[newimg<0]=0
    newimg[newimg>1]=1
    newimg = (newimg*255).astype('uint8')
    return newimg


def fn_timer(function):
  @functools.wraps(function)
  def function_timer(*args, **kwargs):
    t0 = time.time()
    result = function(*args, **kwargs)
    t1 = time.time()
    print ("Total time running %s: %s seconds" %
        (getattr(function,'__name__'), str(t1-t0))
        )
    return result
  return function_timer

@fn_timer
def test_pydicom(name):
     for i in range(100):
        img = read_dcm_to_8bit(name)
@fn_timer
def test_sitk(name):
    for i in range(100):
       img = read_dcm_by_sitk(name)


def main():
    import matplotlib.pyplot as plt
    path = '/home/blake/data/medical/datasets/breast/CBIS-DDSM/Mass-Training_P_00384_RIGHT_MLO/07-20-2016-DDSM-02609/1-full mammogram images-55971/000000.dcm'
    #img = read_dcm_to_8bit(path)

    test_pydicom(path)
    test_sitk(path)

    # convert_dcm = read_dcm_by_pydicom(dn)
    # convert_dcm.PatientName = ""
    # convert_dcm.InstitutionName = ""
    # convert_dcm.PatientBirthDate = ""
    #
    #
    # save_path = 'convert_mm.dcm'
    # convert_dcm.save_as(save_path)
    #
    # image = read_dcm_to_8bit(save_path)
    # cv_show('image', np.squeeze(image), 0.25)
    # cv2.waitKey(0)

if __name__ == '__main__':
    main()



